/*******************************************************************************
 * This file is part of Crunch Graphics.
 *
 * Crunch Graphics is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Graphics is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Graphics.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.graphics.gpu;

import javax.media.opengl.GL;
import java.nio.Buffer;

/** Encapsulates a buffer on the GPU.
 */
public class GpuBuffer extends ReferenceCountedGpuObject {
	/** Description of buffer settings.
	 */
	public static class BufferDescription {
		public int type;        /**< The buffer type.*/
		public long size;       /**< The size of the buffer.*/
		public Buffer data;     /**< The initial data to fill the buffer with.*/
		public int usage;       /**< The usage hint for the buffer.*/

		/** Sets default values.
		 */
		public BufferDescription() {
			type = GL.GL_ARRAY_BUFFER;
			size = 0;
			data = null;
			usage = GL.GL_STATIC_DRAW;
		}
	}

	/** Returns the ID of the underlying buffer.
	 */
	public int getId() {
		return id;
	}

	/** Returns the type of the buffer.
	 */
	public int getType() {
		return type;
	}

	/** Returns the size of the buffer in bytes.
	 */
	public long getSize() {
		return size;
	}

	/** Returns the usage hint for the buffer.
	 */
	public int getUsage() {
		return usage;
	}

	/** Binds the buffer to the location corresponding to its originally specified type.
	 */
	public void bind() {
		getGpuState().bindBuffer(this);
	}

	/** Binds the buffer to the given location.
	 */
	public void bind(int location) {
		getGpuState().bindBuffer(location, this);
	}

	/** Writes data to the buffer, binding it to the location corresponding to its originally specified type.
	 */
	public boolean write(int bufferOffset, long dataSize, Buffer data) {
		return write(type, bufferOffset, dataSize, data);
	}

	/** Writes data to the buffer, binding it to the given location.
	 */
	public boolean write(int location, long bufferOffset, long dataSize, Buffer data) {
		if (bufferOffset + dataSize > size) {
			return false;
		} else {
			bind(location);
			getGpuState().getGl().glBufferSubData(location, bufferOffset, dataSize, data);
			return true;
		}
	}

	/** Orphans the buffer.
	 *
	 *  This is done by calling glBufferData() with a NULL pointer and
	 *  the same size and hints as when the buffer was originally allocated.
	 */
	public void orphan() {
		orphan(type);
	}

	/** Orphans the buffer, binding it to the given location.
	 */
	public void orphan(int location) {
		bind(location);
		getGpuState().getGl().glBufferData(location, size, null, usage);
	}

	GpuBuffer(GpuState state, BufferDescription desc) {
		super(state);
		type = desc.type;
		size = desc.size;
		usage = desc.usage;

		int[] buf = new int[1];
		getGpuState().getGl().glGenBuffers(1, buf, 0);
		id = buf[0];
		bind();
		getGpuState().getGl().glBufferData(type, size, desc.data, usage);
	}

	protected void onRelease() {
		getGpuState().getGl().glDeleteBuffers(1, new int[] { id }, 0);
	}

	private int id;
	private int type;
	private long size;
	private int usage;
}